﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;


namespace Qianke.Core.Common
{
    public static class BytesEncryptHelper
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string EncodeBytes(byte[] source)
        {
            StringBuilder strBuf = new StringBuilder();
            foreach (byte t in source)
            {
                strBuf.Append((char)(((t >> 4) & 0xF) + ((int)'a')));
                strBuf.Append((char)(((t) & 0xF) + ((int)'a')));
            }
            return strBuf.ToString();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] DecodeBytes(string data)
        {
            byte[] bytes = new byte[data.Length / 2];
            for (int i = 0; i < data.Length; i += 2)
            {
                char c = data[i];
                bytes[i / 2] = (byte)((c - 'a') << 4);
                c = data[i + 1];
                bytes[i / 2] += (byte)(c - 'a');
            }
            return bytes;
        }
    }

    public class AESHelper
    {
        private static Aes CreateAes(byte[] key,byte[] iv)
        {
            var aes = Aes.Create();
            aes.Mode = CipherMode.ECB;
            aes.Padding = PaddingMode.PKCS7;

            var tKeyBuffer = new byte[aes.Key.Length];
            Array.Copy(key, tKeyBuffer, Math.Min(key.Length, 32));

            aes.Key = tKeyBuffer;
            if (iv != null)
            {
                var tIVBuffer = new byte[aes.IV.Length];
                aes.Mode = CipherMode.CBC;
                Array.Copy(iv, tIVBuffer, Math.Min(iv.Length,aes.IV.Length));
                aes.IV = tIVBuffer;
            }
            return aes;
        }

        #region 解密 同步
        public static TResult Decrypt<TResult>(byte[] buffer, byte[] key, byte[] iv, Func<Stream, TResult> fnHandResult)
        {
            using (var aes = CreateAes(key,iv))
            {
                using (var ms = new MemoryStream(buffer))
                {
                    using (var aesDecrypt = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
                        return fnHandResult(aesDecrypt);
                }
            }
        }

        public static byte[] Decrypt(byte[] buffer, byte[] key, byte[] iv)
        {
            return Decrypt(buffer, key, iv, aesDecrypt =>
            {
                var tBuffer = new byte[1024];
                var nCount = 0;
                using (var outms = new MemoryStream())
                {
                    while ((nCount = aesDecrypt.Read(tBuffer, 0, 1024)) > 0)
                        outms.Write(buffer, 0, nCount);
                    return outms.ToArray();
                }
            });
        }


        public static TResult Decrypt<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Func<Stream, TResult> fnHandResult)
        {
            if (string.IsNullOrEmpty(key))
                throw new Exception("Aes加密 Key不能为空");
            var keyBuffer = keyEncoding.GetBytes(key);
            var ivBuffer = !string.IsNullOrEmpty(iv) ? keyEncoding.GetBytes(iv) : null;
            return Decrypt(buffer, keyBuffer, ivBuffer, fnHandResult);
        }

        public static string Decrypt(byte[] buffer, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            return Decrypt(buffer, key, iv, keyEncoding, aesStream =>
            {
                using (var sr = new StreamReader(aesStream, resultEncoding))
                    return sr.ReadToEnd();
            });
        }

        public static string DecryptFromByteString(string byteString, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            var buffer = BytesEncryptHelper.DecodeBytes(byteString);
            return Decrypt(buffer, key, iv, keyEncoding, resultEncoding);
        }
        #endregion

        #region 解密  异步
        public static Task<TResult> DecryptAsync<TResult>(byte[] buffer, byte[] key, byte[] iv, Func<Stream, Task<TResult>> fnHandResult)
        {
            return Decrypt(buffer, key, iv, fnHandResult);
        }

        public static Task<byte[]> DecryptAsync(byte[] buffer, byte[] key, byte[] iv)
        {
            return DecryptAsync(buffer, key, iv, async aesDecrypt =>
            {
                var tBuffer = new byte[1024];
                var nCount = 0;
                using (var outms = new MemoryStream())
                {
                    while ((nCount = await aesDecrypt.ReadAsync(tBuffer, 0, 1024)) > 0)
                        await outms.WriteAsync(buffer, 0, nCount);
                    return outms.ToArray();
                }
            });
        }

        public static Task<TResult> DecryptAsync<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Func<Stream, Task<TResult>> fnHandResult)
        {
            if (string.IsNullOrEmpty(key))
                throw new Exception("Aes加密 Key不能为空");
            var keyBuffer = keyEncoding.GetBytes(key);
            var ivBuffer = !string.IsNullOrEmpty(iv) ? keyEncoding.GetBytes(iv) : null;
            return DecryptAsync(buffer, keyBuffer, ivBuffer, fnHandResult);
        }

        public static Task<string> DecryptAsync(byte[] buffer, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            return DecryptAsync(buffer, key, iv, keyEncoding, aesStream =>
            {
                using (var sr = new StreamReader(aesStream, resultEncoding))
                    return sr.ReadToEndAsync();
            });
        }

        public static Task<string> DecryptFromByteStringAsync(string byteString, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            var buffer = BytesEncryptHelper.DecodeBytes(byteString);
            return DecryptAsync(buffer, key, iv, keyEncoding, resultEncoding);
        }
        #endregion



        #region 加密  同步
        public static TResult Encrypt<TResult>(byte[] buffer, byte[] key, byte[] iv, Func<MemoryStream, TResult> fnHandResult)
        {
            using (var aes = CreateAes(key,iv))
            {
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(buffer, 0, buffer.Length);
                        cs.FlushFinalBlock();
                        return fnHandResult(ms);
                    }
                }
            }
        }

        public static TResult Encrypt<TResult, TKey>(byte[] buffer, TKey key, TKey iv, Func<TKey, byte[]> fnHandKey, Func<MemoryStream, TResult> fnHandResult)
        {
            var keyBuffer = fnHandKey(key);
            var ivBuffer = iv != null ? fnHandKey(iv) : null;
            return Encrypt(buffer, keyBuffer, ivBuffer, fnHandResult);
        }

        public static TResult Encrypt<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Func<MemoryStream, TResult> fnHandResult)
        {
            return Encrypt(buffer, key, iv, sv => keyEncoding.GetBytes(sv), fnHandResult);
        }

        public static byte[] Encrypt<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding)
        {
            return Encrypt(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr => sr.ToArray());
        }

        public static string Encrypt<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            return Encrypt(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr => resultEncoding.GetString(sr.ToArray()));
        }

        public static string EncryptToByteString(byte[] buffer, string key, string iv, Encoding keyEncoding)
        {
            return Encrypt(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr =>
            {
                var resultBuffer = sr.ToArray();
                return BytesEncryptHelper.EncodeBytes(resultBuffer);
            });
        }

        public static string EncryptToByteString(string data, string key, string iv, Encoding encoding)
        {
            return Encrypt(encoding.GetBytes(data), key, iv, sv => encoding.GetBytes(sv), sr =>
            {
                var resultBuffer = sr.ToArray();
                return BytesEncryptHelper.EncodeBytes(resultBuffer);
            });
        }
        #endregion


        #region 加密 异步
        public static async Task<TResult> EncryptAsync<TResult>(byte[] buffer, byte[] key, byte[] iv, Func<MemoryStream, Task<TResult>> fnHandResult)
        {
            using (var aes = CreateAes(key, iv))
            {
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        await cs.WriteAsync(buffer, 0, buffer.Length);
                        cs.FlushFinalBlock();
                        return await fnHandResult(ms);
                    }
                }
            }
        }


        public static Task<TResult> EncryptAsync<TResult, TKey>(byte[] buffer, TKey key, TKey iv, Func<TKey, byte[]> fnHandKey, Func<MemoryStream, Task<TResult>> fnHandResult)
        {
            var keyBuffer = fnHandKey(key);
            var ivBuffer = iv != null ? fnHandKey(iv) : null;
            return EncryptAsync(buffer, keyBuffer, ivBuffer, fnHandResult);
        }

        public static Task<TResult> EncryptAsync<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Func<MemoryStream, Task<TResult>> fnHandResult)
        {
            return EncryptAsync(buffer, key, iv, sv => keyEncoding.GetBytes(sv), fnHandResult);
        }

        public static Task<byte[]> EncryptAsync<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding)
        {
            return EncryptAsync(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr => Task.FromResult(sr.ToArray()));
        }

        public static Task<string> EncryptAsync<TResult>(byte[] buffer, string key, string iv, Encoding keyEncoding, Encoding resultEncoding)
        {
            return EncryptAsync(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr => Task.FromResult(resultEncoding.GetString(sr.ToArray())));
        }

        public static Task<string> EncryptToByteStringAsync(byte[] buffer, string key, string iv, Encoding keyEncoding)
        {
            return EncryptAsync(buffer, key, iv, sv => keyEncoding.GetBytes(sv), sr =>
            {
                var resultBuffer = sr.ToArray();
                return Task.FromResult(BytesEncryptHelper.EncodeBytes(resultBuffer));
            });
        }
        #endregion
    }
}
